diff -Nur lmms-0.4.15/include/sample_buffer.h lmms-0.4.15.new/include/sample_buffer.h
--- lmms-0.4.15/include/sample_buffer.h	2013-07-02 13:44:59.699599386 +0000
+++ lmms-0.4.15.new/include/sample_buffer.h	2013-07-02 02:13:32.000000000 +0000
@@ -51,6 +51,15 @@
 	public:
 		handleState( bool _varying_pitch = false );
 		virtual ~handleState();
+		inline const f_cnt_t frameIndex() const
+		{
+		    return m_frameIndex;
+		}
+
+		inline void setFrameIndex( f_cnt_t _index )
+		{
+		    m_frameIndex = _index;
+		}
 
 
 	private:
diff -Nur lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.cpp lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.cpp
--- lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.cpp	2013-07-02 13:44:59.686266104 +0000
+++ lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.cpp	2013-07-02 02:13:40.000000000 +0000
@@ -75,7 +75,9 @@
 	m_startPointModel( 0, 0, 1, 0.0000001f, this, tr( "Start of sample") ),
 	m_endPointModel( 1, 0, 1, 0.0000001f, this, tr( "End of sample" ) ),
 	m_reverseModel( false, this, tr( "Reverse sample" ) ),
-	m_loopModel( false, this, tr( "Loop") )
+	m_loopModel( false, this, tr( "Loop") ),
+	m_stutterModel( false, this, tr( "Stutter" ) ),
+	m_nextPlayStartPoint( 0 )
 {
 	connect( &m_reverseModel, SIGNAL( dataChanged() ),
 				this, SLOT( reverseModelChanged() ) );
@@ -85,6 +87,8 @@
 				this, SLOT( loopPointChanged() ) );
 	connect( &m_endPointModel, SIGNAL( dataChanged() ),
 				this, SLOT( loopPointChanged() ) );
+	connect( &m_stutterModel, SIGNAL( dataChanged() ),
+				this, SLOT( stutterModelChanged() ) ); 	
 }
 
 
@@ -102,10 +106,27 @@
 {
 	const fpp_t frames = _n->framesLeftForCurrentPeriod();
 
+	// Magic key - a frequency < 20 (say, the bottom piano note if using
+	// a A4 base tuning) restarts the start point. The note is not actually
+	// played.
+	if( m_stutterModel.value() == true && _n->frequency() < 20.0 )
+	{
+		m_nextPlayStartPoint = m_sampleBuffer.startFrame();
+		return;
+	}
+	
 	if( !_n->m_pluginData )
 	{
+		if( m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sampleBuffer.endFrame() )
+		{
+			// Restart playing the note if in stutter mode, not in loop mode,
+			// and we're at the end of the sample.
+			m_nextPlayStartPoint = m_sampleBuffer.startFrame();
+		}
 		_n->m_pluginData = new handleState( _n->hasDetuningInfo() );
+		((handleState *)_n->m_pluginData)->setFrameIndex(m_nextPlayStartPoint);
 	}
+	
 
 	if( m_sampleBuffer.play( _working_buffer,
 					(handleState *)_n->m_pluginData,
@@ -115,12 +136,25 @@
 		applyRelease( _working_buffer, _n );
 		instrumentTrack()->processAudioBuffer( _working_buffer,
 								frames,_n );
-		emit isPlaying( _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency() );
+		int framesPosition;
+		if( m_stutterModel.value() == true )
+		{
+			framesPosition = m_nextPlayStartPoint;
+		}
+		else
+		{
+			framesPosition = _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency();
+		}
+		emit isPlaying( framesPosition );
 	}
 	else
 	{
 		emit isPlaying( 0 );
 	}
+	if( m_stutterModel.value() == true )
+	{
+		m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex();
+	}
 }
 
 
@@ -249,6 +283,10 @@
 }
 
 
+void audioFileProcessor::stutterModelChanged()
+{
+	m_nextPlayStartPoint = m_sampleBuffer.startFrame();
+} 
 
 
 void audioFileProcessor::loopPointChanged( void )
@@ -257,6 +295,7 @@
 						( m_sampleBuffer.frames()-1 ) );
 	const f_cnt_t f2 = static_cast<f_cnt_t>( m_endPointModel.value() *
 						( m_sampleBuffer.frames()-1 ) );
+	m_nextPlayStartPoint = f1;
 	m_sampleBuffer.setStartFrame( qMin<f_cnt_t>( f1, f2 ) );
 	m_sampleBuffer.setEndFrame( qMax<f_cnt_t>( f1, f2 ) );
 	emit dataChanged();
@@ -298,7 +337,7 @@
 
 	m_reverseButton = new pixmapButton( this );
 	m_reverseButton->setCheckable( TRUE );
-	m_reverseButton->move( 184, 124 );
+	m_reverseButton->move( 176, 124 ); // 184
 	m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
 							"reverse_on" ) );
 	m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
@@ -311,7 +350,7 @@
 
 	m_loopButton = new pixmapButton( this );
 	m_loopButton->setCheckable( TRUE );
-	m_loopButton->move( 220, 124 );
+	m_loopButton->move( 202, 124 ); // 220
 	m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
 								"loop_on" ) );
 	m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
@@ -325,6 +364,23 @@
 			"This is useful for things like string and choir "
 			"samples." ) );
 
+	m_stutterButton = new pixmapButton( this );
+	m_stutterButton->setCheckable( true );
+	m_stutterButton->move( 228, 124 );
+	m_stutterButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
+								"stutter_on" ) );
+	m_stutterButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
+								"stutter_off" ) );
+	toolTip::add( m_stutterButton,
+				tr( "Continue sample playback across notes" ) );
+	m_stutterButton->setWhatsThis(
+		tr( "Enabling this option makes the sample continue playing "
+			"across different notes - if you change pitch, or the note "
+			"length stops before the end of the sample, then the next "
+			"note played will continue where it left off. To reset the "
+			"playback to the start of the sample, insert a note at the bottom "
+			"of the keyboard (< 20 Hz)") ); 	
+
 	m_ampKnob = new knob( knobStyled, this );
 	m_ampKnob->setVolumeKnob( TRUE );
 	m_ampKnob->move( 17, 108 );
@@ -504,6 +560,7 @@
 	m_endKnob->setModel( &a->m_endPointModel );
 	m_reverseButton->setModel( &a->m_reverseModel );
 	m_loopButton->setModel( &a->m_loopModel );
+	m_stutterButton->setModel( &a->m_stutterModel );
 	sampleUpdated();
 }
 
@@ -522,8 +579,7 @@
 	m_endKnob( 0 ),
 	m_isDragging( false ),
 	m_reversed( false ),
-	m_framesPlayed( 0 ),
-	m_animation(configManager::inst()->value("ui", "animateafp").toInt())
+	m_framesPlayed( 0 )
 {
 	setFixedSize( _w, _h );
 	setMouseTracking( true );
@@ -546,11 +602,13 @@
 	const f_cnt_t nb_frames = m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame();
 	if( nb_frames < 1 )
 	{
+/* MCA */printf("reset framesPlayed to 0\n");
 		m_framesPlayed = 0;
 	}
 	else
 	{
 		m_framesPlayed = _frames_played % nb_frames;
+/* MCA */printf("isPlaying: framesPlayed = %d\n", m_framesPlayed);
 	}
 	update();
 }
@@ -702,7 +760,7 @@
 			QColor( 255, 255, 0, 70 )
 		);
 
-		if( m_framesPlayed && m_animation)
+		if( m_framesPlayed )
 		{
 			const int played_width_px = m_framesPlayed
 				/ double( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() )
@@ -933,12 +991,8 @@
 		return;
 	}
 	const double v = double( _frames ) / m_sampleBuffer.frames();
-	if(m_startKnob) {
-		m_startKnob->slideBy( v, false );
-	}
-	if(m_endKnob) {
-		m_endKnob->slideBy( v, false );
-	}
+	m_startKnob->slideBy( v, false );
+	m_endKnob->slideBy( v, false );
 }
 
 
diff -Nur lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.h lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.h
--- lmms-0.4.15/plugins/audio_file_processor/audio_file_processor.h	2013-07-02 13:44:59.686266104 +0000
+++ lmms-0.4.15.new/plugins/audio_file_processor/audio_file_processor.h	2013-07-02 02:13:44.000000000 +0000
@@ -74,6 +74,7 @@
 	void reverseModelChanged();
 	void ampModelChanged();
 	void loopPointChanged();
+	void stutterModelChanged();
 
 
 signals:
@@ -90,6 +91,10 @@
 	FloatModel m_endPointModel;
 	BoolModel m_reverseModel;
 	BoolModel m_loopModel;
+	BoolModel m_stutterModel;
+
+	f_cnt_t m_nextPlayStartPoint;
+
 
 	friend class AudioFileProcessorView;
 
@@ -131,6 +136,7 @@
 	pixmapButton * m_openAudioFileButton;
 	pixmapButton * m_reverseButton;
 	pixmapButton * m_loopButton;
+	pixmapButton * m_stutterButton;
 
 } ;
 
@@ -234,7 +240,7 @@
 	draggingType m_draggingType;
 	bool m_reversed;
 	f_cnt_t m_framesPlayed;
-	bool m_animation;
+
 
 public:
 	AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf );
